//
// (C) Copyright 2009 Irantha Suwandarathna (irantha@gmail.com)
// All rights reserved.
//

/* Copyright (c) 2001-2008, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use _in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions _in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer _in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


using System;
using System.IO;
using System.Text;



namespace EffiProzDB.Lib
{

    /**
     * This class is a replacement for both java.io.ByteArrayOuputStream
     * (without synchronization) and java.io.DataOutputStream
     *
     * @author fredt@users
     * @version 1.7.2
     * @since 1.7.0
     */
    public class HsqlByteArrayOutputStream
    {

        protected byte[] buf;
        protected int count;

        public HsqlByteArrayOutputStream()
            : this(128)
        {

        }

        public HsqlByteArrayOutputStream(int size)
        {
            if (size < 128)
            {
                size = 128;
            }

            buf = new byte[size];
        }

        public HsqlByteArrayOutputStream(byte[] buffer)
        {
            buf = buffer;
        }

        // methods that implement dataOutput
        public void writeShort(int v)
        {
            ensureRoom(2);

            byte[] bits = BitConverter.GetBytes((short)v);
            buf[count++] = bits[0];
            buf[count++] = bits[1];
            
        }

        public void writeDateTime(DateTime v)
        {
            ensureRoom(8);
            long lv = v.ToBinary();
            writeLong(lv);
        }

        public void writeInt(int v)
        {            
            ensureRoom(4);

            byte[] bits = BitConverter.GetBytes(v);
            buf[count++] = bits[0];
            buf[count++] = bits[1];
            buf[count++] = bits[2];
            buf[count++] = bits[3];
        }

        public void writeDecimal(Decimal v)
        {
   
            int[] bits = Decimal.GetBits(v);
            writeInt(bits[0]);
            writeInt(bits[1]);
            writeInt(bits[2]);
            writeInt(bits[3]);    
        }

        public void writeLong(long v)
        {
            ensureRoom(8); 

            byte[] bits = BitConverter.GetBytes(v);
            buf[count++] = bits[0];
            buf[count++] = bits[1];
            buf[count++] = bits[2];
            buf[count++] = bits[3];
            buf[count++] = bits[4];
            buf[count++] = bits[5];
            buf[count++] = bits[6];
            buf[count++] = bits[7];
        }

        public void writeBytes(string s)
        {

            int len = s.Length;

            ensureRoom(len);

            for (int i = 0; i < len; i++)
            {
                buf[count++] = (byte)s[i];
            }
        }

        public void writeFloat(float v)
        {
            writeLong(BitConverter.DoubleToInt64Bits(v));
        }

        public void writeDouble(double v)
        {
            writeLong(BitConverter.DoubleToInt64Bits(v));
        }

        public void writebool(bool v)
        {
            ensureRoom(1);

            buf[count++] = (byte)(v ? 1 : 0);
        }

        public void writeByte(int v)
        {
            ensureRoom(1);

            buf[count++] = (byte)(v);
        }

        public void writeChar(int v)
        {
            ensureRoom(2);

            byte[] bits = BitConverter.GetBytes((char)v);
            buf[count++] = bits[0];
            buf[count++] = bits[1];                 
        }

        public void writeChars(string s)
        {

            int len = s.Length;

            ensureRoom(len * 2);           

            for (int i = 0; i < len; i++)
            {
                int v = s[i];

                byte[] bits = BitConverter.GetBytes((char)v);
                buf[count++] = bits[0];
                buf[count++] = bits[1]; 
            }
        }

        public void writeUTF(string str)
        {
           
            int len = str.Length;

            if (len > 0xffff)
            {
                throw new Exception();
            }

            byte[] bUTF8 = Encoding.UTF8.GetBytes(str);

            ensureRoom(bUTF8.Length);

            //
            int initpos = count;

            count += 2;

            write(bUTF8, 0, bUTF8.Length);      

            int bytecount = count - initpos - 2;

            if (bytecount > 0xffff)
            {
                count = initpos;
                throw new Exception();
            }

            byte[] bits = BitConverter.GetBytes((short)bytecount);

            buf[initpos++] = bits[0];
            buf[initpos] = bits[1];
        }

        /**
         * does nothing
         */
        public void flush()
        {

        }

        // methods that extend java.io.OutputStream
        public void write(int b)
        {
            ensureRoom(1);

            buf[count++] = (byte)b;
        }

        public void write(byte[] b)
        {
            write(b, 0, b.Length);
        }

        public void write(byte[] b, int off, int len)
        {

            ensureRoom(len);
            Array.Copy(b, off, buf, count, len);

            count += len;
        }

        public void writeTo(Stream outs)
        {
            outs.Write(buf, 0, count);
        }

        public virtual void reset()
        {
            count = 0;
        }

        public byte[] toByteArray()
        {

            byte[] newbuf = new byte[count];

            Array.Copy(buf, 0, newbuf, 0, count);

            return newbuf;
        }

        public int size()
        {
            return count;
        }

        public override string ToString()
        {
            return System.Text.Encoding.UTF8.GetString(buf, 0, count);
        }

        public string ToString(string enc)
        {
            Encoding encoding = Encoding.GetEncoding(enc);
            return encoding.GetString(buf, 0, count);           
        }

        public void close() { }

        // additional public methods not _in similar java.util classes
        public void fill(int b, int len)
        {

            ensureRoom(len);

            for (int i = 0; i < len; i++)
            {
                buf[count++] = (byte)b;
            }
        }

        public byte[] getBuffer()
        {
            return this.buf;
        }

        protected virtual void ensureRoom(int extra)
        {

            int newcount = count + extra;
            int newsize = buf.Length;

            if (newcount > newsize)
            {
                while (newcount > newsize)
                {
                    newsize *= 2;
                }

                byte[] newbuf = new byte[newsize];

                Array.Copy(buf, 0, newbuf, 0, count);

                buf = newbuf;
            }
        }

        public virtual void reset(int newSize)
        {

            count = 0;

            if (newSize > buf.Length)
            {
                buf = new byte[newSize];
            }
        }
    }
}
